index.ts ➔ generator   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 15
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 14
dl 0
loc 15
ccs 11
cts 11
cp 1
crap 5
rs 9.2333
c 0
b 0
f 0
1
/**
2
 * Generator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) function
3
 * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) that produces a sequence of IDs.
4
 *
5
 * The sequence is baed on a list of characters (_sequence_ parameter) and has the lenght based on an initial ID (_initialOrLength_ parameter when
6
 * a string is passed) or on a length o characters (_initialOrLength_ parameter when a number is passed).
7
 *
8
 * A generator function can be iterated with a 'for of', that will return the next ID for each iteration or can be called using "next()", that
9
 * will return an object like { value: <the ID>, done: <boolean that is true when the sequence is finished> }.
10
 * @param initialOrLength The __initial ID string__ if you want to __start the generation from a certain point__ or the __length of the ID__ to
11
 * __start from the first ID__. (default = 5)
12
 *
13
 * Example: _initialOrLength = '00'_ will produce a __sequence from '01' on__. The passed current ID is skipped.
14
 * Example: _initialOrLength = 2_ will __start from '00'__, as the sequence will start from the beginning of the _sequence_ with the specified length.
15
 * @param sequence The string with the sequence of characters to produce the IDs. The order of the characters mather to the result.
16
 * (default = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
17
 * @example const code = idGenerator('28')
18
 * code.next() // { value: '29', done: false }
19
 * code.next() // { value: '2A', done: false }
20
 * code.next() // { value: '2B', done: false }
21
 * @example const code = idGenerator('ZZ')
22
 * code.next() // { value: undefined, done: true }
23
 * @example const code = idGenerator(2, '01')
24
 * code.next() // { value: '00', done: false }
25
 * code.next() // { value: '01', done: false }
26
 * code.next() // { value: '10', done: false }
27
 * code.next() // { value: '11', done: false }
28
 * code.next() // { value: undefined, done: true }
29
 * @example const code = idGenerator('4', '0123456')
30
 * for (const str of code) {
31
 *    console.log(str)
32
 * } // 5, 6
33
 * @example const code = idGenerator(1, '0123456')
34
 * for (const str of code) {
35
 *    console.log(str)
36
 * } // 0, 1, 2, 4, 5, 6
37
 */
38 14
export default function sequentialIdGenerator (initialOrLength: string | number = 5, sequence = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'): Generator<string, void, unknown> {
39 14
  if (initialOrLength === 0 || initialOrLength === '') {
40 2
    console.error('initialOrLength must be bigger than 0 and can\'t be an empty string')
41 2
    throw new Error('initialOrLength must be bigger than 0 and can\'t be an empty string')
42
  }
43 12
  if (typeof initialOrLength === 'string') {
44 9
    for (let index = 0; index < initialOrLength.length; index++) {
45 1175
      const char = initialOrLength[index]
46 1175
      if (!sequence.includes(char)) {
47 1
        console.error('initialOrLength contains an ID with characters that couldn\'t be found in the sequence')
48 1
        throw new Error('initialOrLength contains an ID with characters that couldn\'t be found in the sequence')
49
      }
50
    }
51
  }
52 11
  return generator(initialOrLength, sequence)
53
}
54
55 11
function * generator (initialOrLength: string | number, sequence: string) {
56 11
  let currentId = typeof initialOrLength === 'string' ? nextId(initialOrLength, sequence) : sequence[0].repeat(initialOrLength)
57 11
  const total = Math.pow(currentId.length, sequence.length)
58
59 11
  let index = 0
60
61 11
  while (index < total) {
62 21
    if (currentId === sequence[0].repeat(currentId.length) && typeof initialOrLength === 'string') break
63 18
    yield currentId
64 15
    if (currentId === sequence[sequence.length - 1].repeat(currentId.length)) {
65 5
      break
66
    }
67 10
    currentId = nextId(currentId, sequence)
68 10
    index++
69
  }
70
}
71
72
/**
73
 * Generates the next ID
74
 * @param id The current id
75
 * @param sequence The string with the sequence of characters to produce the IDs. The order of the characters mather to the result.
76
 */
77 18
function nextId (id: string, sequence: string): string {
78 18
  let index = id.length - 1
79 18
  let nextId = ''
80 18
  let rest = 0
81
82 18
  while (index >= 0) {
83
    // Character is added for the last char and if there is a rest from previus chars
84 1345
    if (index === id.length - 1 || rest) {
85 38
      const increment = incrementChar(id[index], sequence)
86 38
      nextId = increment.nextChar + nextId
87 38
      rest = increment.rest
88
    } else {
89 1307
      nextId = id[index] + nextId
90 1307
      rest = 0
91
    }
92 1345
    index--
93
  }
94
95 18
  return nextId
96
}
97
98
/**
99
 * Increment the character, returning the "rest" in case the character is the last in the sequence
100
 * @param char The character to be incremented
101
 * @param sequence The sequence of characters
102
 */
103 38
function incrementChar (char: string, sequence: string) {
104 38
  if (sequence.indexOf(char) === sequence.length - 1) {
105 23
    return {
106
      nextChar: sequence[0],
107
      rest:     1,
108
    }
109
  } else {
110 15
    return {
111
      nextChar: sequence[sequence.indexOf(char) + 1],
112
      rest:     0,
113
    }
114
  }
115
}
116